/**
 * DVN: Distributed Virtual Network Simulation Platform
 * Copyright (c) 2006-2009 Tyson Malchow <tyson.malchow@gmail.com>
 *
 * This file is part of the DVN distributed virtual network simulation
 * program.
 *
 * DVN is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * DVN is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with DVN.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include "sysconfig.h"
#include "hashtable.h"

sysconfig_val_t sysconfig_defaults[] = {
		{"sim_nodetrack_hashsize", SYSCONFIG_INT, {.int_val=128}},
		{"sim_nodetrack_hashgrowth", SYSCONFIG_FLOAT, {.float_val=0.9f}},

		{"sim_modtrack_hashsize", SYSCONFIG_INT, {.int_val=128}},
		{"sim_modtrack_hashgrowth", SYSCONFIG_FLOAT, {.float_val=0.9f}},

		{"sim_nettrack_hashsize", SYSCONFIG_INT, {.int_val=128}},
		{"sim_nettrack_hashgrowth", SYSCONFIG_FLOAT, {.float_val=0.9f}},

		{"vci_network_hashsize", SYSCONFIG_INT, {.int_val=128}},
		{"vci_network_hashgrowth", SYSCONFIG_FLOAT, {.float_val=0.9f}},

		{"vci_mailbox_hashsize", SYSCONFIG_INT, {.int_val=65536}},
		{"vci_mailbox_hashgrowth", SYSCONFIG_FLOAT, {.float_val=0.9f}},

		{"vci_rcnn_hashsize", SYSCONFIG_INT, {.int_val=32}},
		{"vci_rcnn_hashgrowth", SYSCONFIG_FLOAT, {.float_val=0.9f}},

		{"vci_evtracker_size", SYSCONFIG_INT, {.int_val=65536}},
		{"vci_evtracker_granularity", SYSCONFIG_INT, {.int_val=1}},

		{"dtimer_evtracker_size", SYSCONFIG_INT, {.int_val=65536}},
		{"dtimer_evtracker_granularity", SYSCONFIG_INT, {.int_val=1}},

		{"dtimer_tset_hashsize", SYSCONFIG_INT, {.int_val=65536}},
		{"dtimer_tset_hashgrowth", SYSCONFIG_FLOAT, {.float_val=0.9f}},

		{"pipecloud_pp_size", SYSCONFIG_INT, {.int_val=131072}},

		{"event_tracker_size", SYSCONFIG_INT, {.int_val=65536}},
		{"event_tracker_granularity", SYSCONFIG_INT, {.int_val=1}},

		{"max_workers_per_slave", SYSCONFIG_INT, {.int_val=8}},

		{""}
};

sysconfig_t sysconfig;

void sysconfig_init(void)  {
	int i = 0;
	sysconfig.data = hashtable_create(32, 0.9f);

	/* load up all the defaults */
	while (sysconfig_defaults[i].name[0]) {
		switch(sysconfig_defaults[i].type) {
			case SYSCONFIG_INT:
				sysconfig_set_int(sysconfig_defaults[i].name, sysconfig_defaults[i].v.int_val);
				break;

			case SYSCONFIG_FLOAT:
				sysconfig_set_float(sysconfig_defaults[i].name, sysconfig_defaults[i].v.float_val);
				break;

			case SYSCONFIG_STRING:
				sysconfig_set_string(sysconfig_defaults[i].name, sysconfig_defaults[i].v.string_val);
				break;
		}
		i++;
	}
}

int sysconfig_get_int(char * param) {
	sysconfig_val_tp v = hashtable_get(sysconfig.data, adler32_hash(param));
	int rv = 0;

	if(!v)
		return 0;

	switch(v->type) {
		case SYSCONFIG_INT:
			rv = v->v.int_val;
			break;
		case SYSCONFIG_FLOAT:
			rv = ((int)v->v.float_val);
			break;
	}

	return rv;
}
float sysconfig_get_float(char * param) {
	sysconfig_val_tp v = hashtable_get(sysconfig.data, adler32_hash(param));
	float rv = 0.0f;

	if(!v)
		return 0;

	switch(v->type) {
		case SYSCONFIG_INT:
			rv = ((float)v->v.int_val);
			break;
		case SYSCONFIG_FLOAT:
			rv = v->v.float_val;
			break;
	}

	return rv;
}
char * sysconfig_get_string(char * param) {
	static char temp[64];
	sysconfig_val_tp v = hashtable_get(sysconfig.data, adler32_hash(param));
	char * rv = "";

	if(!v)
		return 0;

	switch(v->type) {
		case SYSCONFIG_INT:
			sprintf(temp, "%d", v->v.int_val);
			rv = temp;
			break;
		case SYSCONFIG_FLOAT:
			sprintf(temp, "%f", v->v.float_val);
			rv = temp;
			break;
		case SYSCONFIG_STRING:
			rv = v->v.string_val;
			break;
	}

	return rv;
}

void sysconfig_set_int(char * param, int v) {
	int key = adler32_hash(param);
	sysconfig_val_tp val = hashtable_get(sysconfig.data, key);

	if(!val) {
		val = malloc(sizeof(*val));
		strncpy(val->name, param, sizeof(val->name)); val->name[sizeof(val->name)-1] = 0;
		hashtable_set(sysconfig.data, key, val);
	}

	val->type = SYSCONFIG_INT;
	val->v.int_val = v;
}

void sysconfig_set_string(char * param, char * v) {
	int key = adler32_hash(param);
	sysconfig_val_tp val = hashtable_get(sysconfig.data, key);

	if(!val) {
		val = malloc(sizeof(*val));
		strncpy(val->name, param, sizeof(val->name)); val->name[sizeof(val->name)-1] = 0;
		hashtable_set(sysconfig.data, key, val);
	}

	strncpy(val->v.string_val, v, sizeof(val->v.string_val));
	val->v.string_val[sizeof(val->v.string_val)-1] = 0;
	val->type = SYSCONFIG_STRING;
}

void sysconfig_set_float(char * param, float v) {
	int key = adler32_hash(param);
	sysconfig_val_tp val = hashtable_get(sysconfig.data, key);

	if(!val) {
		val = malloc(sizeof(*val));
		strncpy(val->name, param, sizeof(val->name)); val->name[sizeof(val->name)-1] = 0;
		hashtable_set(sysconfig.data, key, val);
	}

	val->v.float_val = v;
	val->type = SYSCONFIG_FLOAT;
}

int sysconfig_determine_type(char * str) {
	char dot_found = 0;
	char * curletter = str;
	int len;

	while(*curletter) {
		if(*curletter == '.') {
			if(dot_found)
				return SYSCONFIG_STRING;
			dot_found  = 1;

		} else if(!isdigit(*curletter))
			return SYSCONFIG_STRING;
		curletter++;
	}

	if(dot_found) {
		if((len=strlen(str)) > 1 && str[len-1]!='.')
			return SYSCONFIG_FLOAT;
		else
			return SYSCONFIG_STRING;
	} else
		return SYSCONFIG_INT;
}

void sysconfig_import_config(char * in_config_data) {
	/* woohoo */
	static char line_delims[] = "\n\r";
	static char name_delims[] = " \t\r\n";
	char * config_data, * tok, * nameptr, * valptr;
	char * full_ptr, * line_ptr;
	int len, val_len;

	if(!in_config_data)
		return;

	len = strlen(in_config_data);
	config_data = malloc(len + 1);
	strcpy(config_data, in_config_data);

	tok = strtok_r(config_data, line_delims, &full_ptr);
	while(tok) {
		nameptr = strtok_r(tok, name_delims, &line_ptr);
		valptr = strtok_r(NULL, line_delims, &line_ptr);

		/* remove trailing \t and spaces from val .. */
		while(*valptr&&(*valptr==' '||*valptr=='\t')) valptr++;
		val_len = strlen(valptr);
		while(val_len > 0 && (valptr[val_len-1] == ' ' || valptr[val_len-1] == '\t'))
			valptr[--val_len] = 0;

		if(strlen(valptr) == 0)
			continue;

		/* figure out what the value is, then save it to the configuration */
		switch(sysconfig_determine_type(valptr)) {
			case SYSCONFIG_INT:
				sysconfig_set_int(nameptr, atoi(valptr));
				break;
			case SYSCONFIG_FLOAT:
				sysconfig_set_float(nameptr, atof(valptr));
				break;
			case SYSCONFIG_STRING:
				sysconfig_set_string(nameptr, valptr);
				break;
		}

		tok = strtok_r(NULL, line_delims, &full_ptr);
	}

	free(config_data);

	return;
}

void sysconfig_export_walk(void * d, int key) {
	static char temp_buffer[512];

	sysconfig_val_tp val = d;
	if(val) {
		switch(val->type) {
			case SYSCONFIG_INT:
				sysconfig.exported_config_size += snprintf(temp_buffer, sizeof(temp_buffer), "%-40s %d\n", val->name, val->v.int_val);
				break;
			case SYSCONFIG_FLOAT:
				sysconfig.exported_config_size += snprintf(temp_buffer, sizeof(temp_buffer), "%-40s %f\n", val->name, val->v.float_val);
				break;
			case SYSCONFIG_STRING:
				sysconfig.exported_config_size += snprintf(temp_buffer, sizeof(temp_buffer), "%-40s %s\n", val->name, val->v.string_val);
				break;
		}
		temp_buffer[sizeof(temp_buffer) - 1] = 0;
		strncat(sysconfig.exported_config, temp_buffer, sizeof(sysconfig.exported_config) - sysconfig.exported_config_size - 1);
		sysconfig.exported_config[sizeof(sysconfig.exported_config) - 1] = 0;
	}
}

char * sysconfig_export_config(void) {
	sysconfig.exported_config[0] = 0;
	sysconfig.exported_config_size = 0;
	hashtable_walk(sysconfig.data, &sysconfig_export_walk);
	return sysconfig.exported_config;
}

void sysconfig_cleanup(void) {
	hashtable_destroy(sysconfig.data);
}
